/************************************************
 * Author       :   XXY
 * Blog         :   https://www.cnblogs.com/jzyl
 * Gitee        :   https://gitee.com/xiaobaichong
 * Email        :   1047185209@QQ.com
 * FileName     :   CarouselCanvas.cs
 * CreateData   :   2024/2/28 21:41:37
 * UnityVersion :   2021.3.30f1c1
 * Description  :   UI 轮播图
************************************************/

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.Events;
using UnityEngine.EventSystems;

namespace Main.Framework
{
    public class CarouselCanvas : MonoBehaviour
    {
        [SerializeField, Range(1, 10)] float autoWaitPlayTime = 5;    // 自动播放等待时间间隔
        [SerializeField] AnimationCurve animationCurve;               // 动画曲线(0-1) 曲线
        [Header("轮播图 图片")]
        [SerializeField] List<Sprite> _sprites;


        RectTransform MaskWindow;
        Button LeftBtn;
        Button RightBtn;
        GameObject NavPointPrefab;


        float width;    // 轮播区域宽度
        List<Toggle> Toggles = new List<Toggle>();    // 导航点
        RectTransform[] preTrans;                     // 两张交换轮播图片 Transform
        Image[] preImgs;                               // 两张预览图
        Vector2 curPrePos;                            // 当前预览图位置
        int curImgsIndex;                            // 当前展示 两个预览图哪个 （0/1）
        int currentListIndex;                       // 当前展示集合中图片的索引
        bool isDraging;                             // 是否正在拖拽
        bool isAnimation;                           // 是否在移动动画中
        float autoPlayTimer;                        // 自动播放计时器

        /// <summary>
        /// 交换完成事件
        /// </summary>
        public UnityEvent<int> OnSwapEvent = new UnityEvent<int>();
        /// <summary>
        /// 点击图片事件
        /// </summary>
        public UnityEvent<int> OnClickEvent = new UnityEvent<int>();

        private void Awake()
        {
            MaskWindow = transform.Find("MaskWindow").GetComponent<RectTransform>();
            LeftBtn = transform.Find("MaskWindow/LeftBtn").GetComponent<Button>();
            RightBtn = transform.Find("MaskWindow/RightBtn").GetComponent<Button>();
            NavPointPrefab = transform.Find("MaskWindow/NavPoints/NavPointPrefab").gameObject;
            RectTransform pictures = transform.Find("MaskWindow/Pictures").GetComponent<RectTransform>();
            width = pictures.rect.width;
            preTrans = new RectTransform[2];
            preImgs = new Image[2];
            preTrans[0] = (RectTransform)pictures.GetChild(0);
            preTrans[1] = (RectTransform)pictures.GetChild(1);
            preImgs[0] = pictures.GetChild(0).GetComponent<Image>();
            preImgs[1] = pictures.GetChild(1).GetComponent<Image>();
        }
        private void Start()
        {
            NavPointPrefab.SetActive(false);
            LeftBtn.onClick.AddListener(OnPreClick);
            RightBtn.onClick.AddListener(OnNextClick);
            EventTrigger trigger;
            if (!MaskWindow.TryGetComponent<EventTrigger>(out trigger))
            {
                trigger = MaskWindow.gameObject.AddComponent<EventTrigger>();
            }
            // 点击
            EventTrigger.Entry click = new EventTrigger.Entry();
            click.eventID = EventTriggerType.PointerClick;
            click.callback.AddListener(OnPointerClick);
            trigger.triggers.Add(click);
            // 开始拖拽
            EventTrigger.Entry beginDrag = new EventTrigger.Entry();
            beginDrag.eventID = EventTriggerType.BeginDrag;
            beginDrag.callback.AddListener(OnBeginDrag);
            trigger.triggers.Add(beginDrag);
            // 拖拽
            EventTrigger.Entry draging = new EventTrigger.Entry();
            draging.eventID = EventTriggerType.Drag;
            draging.callback.AddListener(OnDrag);
            trigger.triggers.Add(draging);
            // 结束拖拽
            EventTrigger.Entry endDrag = new EventTrigger.Entry();
            endDrag.eventID = EventTriggerType.EndDrag;
            endDrag.callback.AddListener(OnEndDrag);
            trigger.triggers.Add(endDrag);
            // 构建
            Rebuild(_sprites);
        }


        private void OnEnable()
        {
            autoPlayTimer = 0;
            isAnimation = false;
            isDraging = false;
            curPrePos = Vector2.zero;
        }
        /// <summary>
        /// 重新构建轮播图
        /// </summary>
        /// <param name="sprites">轮播图序列</param>
        public void Rebuild(List<Sprite> sprites)
        {
            _sprites = sprites;
            if (_sprites == null || _sprites.Count < 2) return;
            // 删除
            Toggles.Clear();
            int childCount = NavPointPrefab.transform.parent.childCount;
            for (int i = 1; i < childCount; i++)
            {
                DestroyImmediate(NavPointPrefab.transform.parent.GetChild(1).gameObject);
            }
            // 生成
            for (int i = 0; i < _sprites.Count; i++)
            {
                int index = i;
                GameObject navToggleObj = Instantiate(NavPointPrefab, NavPointPrefab.transform.parent);
                navToggleObj.SetActive(true);
                Toggle toggle = navToggleObj.GetComponent<Toggle>();
                Toggles.Add(toggle);
                toggle.onValueChanged.AddListener(b => OnNavToggle(b, index));
            }
            Toggles[0].SetIsOnWithoutNotify(true);
            autoPlayTimer = 0;
            curImgsIndex = 0;
            currentListIndex = 0;
            preImgs[0].sprite = _sprites[0];
        }
        /// <summary>
        /// 当前展示的图片序号
        /// </summary>
        public int GetCurrentIndex => currentListIndex;
        /// <summary>
        /// 获取曲线移动速度
        /// </summary>
        private float GetCurveMoveSpeed => animationCurve.Evaluate(Mathf.Abs(curPrePos.x) / width);
        /// <summary>
        /// 获取要被交换图片序号
        /// </summary>
        /// <param name="isNext"></param>
        /// <returns>交换图片索引</returns>
        private int GetSwapImgIndex(bool isNext)
        {
            int ptr = currentListIndex + (isNext ? 1 : -1);
            if (ptr < 0) return _sprites.Count - 1;
            else if (ptr > _sprites.Count - 1) return 0;
            else return ptr;
        }

        /// <summary>
        /// 导航点被点击
        /// </summary>
        /// <param name="indexToggle"></param>
        private void OnNavToggle(bool isOn, int indexToggle)
        {
            if (isOn)
            {
                JumpMoveSwap(indexToggle);
            }
        }
        /// <summary>
        /// 播放下一张
        /// </summary>
        private void OnNextClick()
        {
            if (!isAnimation)
            {
                StartCoroutine(MoveSwap(true));
            }
        }
        /// <summary>
        /// 播放上一周
        /// </summary>
        private void OnPreClick()
        {
            if (!isAnimation)
            {
                StartCoroutine(MoveSwap(false));
            }
        }
        /// <summary>
        /// 移动到指定图片序号位置
        /// </summary>
        /// <param name="index"></param>
        private void MoveToPicturesIndex(int index)
        {

        }
        private void Update()
        {
            // 更新位置
            preTrans[curImgsIndex].anchoredPosition = curPrePos;
            preTrans[Mathf.Abs(1 - curImgsIndex)].anchoredPosition = curPrePos + (curPrePos.x > 0 ? -1 : 1) * Vector2.right * width;
            // 自动播放
            if (!isAnimation && !isDraging)
            {
                autoPlayTimer += Time.deltaTime;

                if (autoPlayTimer > autoWaitPlayTime)
                {
                    autoPlayTimer = 0;
                    StartCoroutine(MoveSwap(true));
                }
            }
            else
            {
                autoPlayTimer = 0;
            }
        }
        /// <summary>
        /// 移动交换图片
        /// </summary>
        /// <param name="isNext">是否是下一张，否则上一张</param>
        /// <returns></returns>
        private IEnumerator MoveSwap(bool isNext)
        {
            isAnimation = true;
            int swapIndex = GetSwapImgIndex(isNext);
            preImgs[curImgsIndex].sprite = _sprites[currentListIndex];
            preImgs[Mathf.Abs(1 - curImgsIndex)].sprite = _sprites[swapIndex];
            float targetX = (isNext ? -1 : 1) * width;
            while (curPrePos.x != targetX)
            {
                yield return null;
                curPrePos.x = Mathf.MoveTowards(curPrePos.x, targetX, GetCurveMoveSpeed);
            }
            //交换
            curImgsIndex = Mathf.Abs(1 - curImgsIndex);
            currentListIndex = swapIndex;
            curPrePos.x = 0;
            Toggles[currentListIndex].SetIsOnWithoutNotify(true);
            OnSwapEvent?.Invoke(currentListIndex);
            isAnimation = false;
        }
        /// <summary>
        /// 返回至原来位置
        /// </summary>
        /// <returns></returns>
        private IEnumerator ReturnPos()
        {
            isAnimation = true;
            while (curPrePos.x != 0)
            {
                yield return null;
                curPrePos.x = Mathf.MoveTowards(curPrePos.x, 0, GetCurveMoveSpeed);
            }
            isAnimation = false;
        }
        /// <summary>
        /// 跳跃到达目标序号位置
        /// </summary>
        /// <param name="toIndex"></param>
        private void JumpMoveSwap(int toIndex)
        {
            currentListIndex = toIndex;
            preImgs[curImgsIndex].sprite = _sprites[currentListIndex];
            OnSwapEvent?.Invoke(currentListIndex);
        }

        private void OnPointerClick(BaseEventData eventData)
        {
            PointerEventData pointerData = (PointerEventData)eventData;
            if (RectTransformUtility.RectangleContainsScreenPoint(MaskWindow, pointerData.position, null, new Vector4(50, 50, 50, 50)))
            {

                Debug.Log(pointerData.position);
                OnClickEvent?.Invoke(currentListIndex);
            }
        }

        private void OnBeginDrag(BaseEventData eventData)
        {
            isDraging = true;
        }

        private void OnDrag(BaseEventData eventData)
        {
            if (!isAnimation)
            {
                int swapIndex = GetSwapImgIndex(curPrePos.x < 0);
                preImgs[Mathf.Abs(1 - curImgsIndex)].sprite = _sprites[swapIndex];
                PointerEventData pointerData = (PointerEventData)eventData;
                curPrePos.x = Mathf.Clamp(pointerData.delta.x + curPrePos.x, -width, width);
            }
        }

        private void OnEndDrag(BaseEventData eventData)
        {
            if (curPrePos.x < -width / 2)
            {
                StartCoroutine(MoveSwap(true));
            }
            else if (curPrePos.x > width / 2)
            {
                StartCoroutine(MoveSwap(false));
            }
            else
            {
                StartCoroutine(ReturnPos());
            }
            isDraging = false;
        }
    }
}
